wait() & waitpid() [system call]

fork() & exec()
리눅스 프로세스 실행
    1. 부모 프로세스로부터 새로운 프로세스 공간을 만들고 부모 프로세스 데이터 복사(fork)
    2. 새로운 프로세스를 위한 바이너리를 새로운 프로세스 공간에 덮어씌움(exec)

fork와 exec를 순차적으로 실행함으로써, 프로세스(자식 프로세스)를 생성-Linux/Unix 프로세스 생성 방식
wait() 시스템콜
wait() 함수를 사용하면, fork() 함수 호출시, 자식 프로세스가 종료할 때까지, 부모 프로세스가 기다린다.
자식 프로세스와 부모 프로세스의 동기화, 부모 프로세스가 자식 프로세스보다 먼저 죽는 경우를 막기 위해 사용(고아 프로세스)
    원래 자식 프로세스가 끝날 때, 자식 프로세스의 정보를 메모리에 저장해둔다.
    만일 부모 프로세스가 먼저 끝나 자식 프로세스의 정보를 확인하지 못하면, 리소스를 계속 차지하고 있게 된다.(좀비 프로세스)
    -> 부모 프로세스가 자식 프로세스보다 먼저 끝나면 안됨
Wait System Call in C - GeeksforGeeks
Child Process의 exit() 함수가 실행되면, IPC signal(‘SIGCHLD’)를 통해 Parent Process resumes
#include <sys/wait.h>
pid_t wait(int *statloc); //: PID, : -1
wait_test.c
int main(void){
int pid;
int child_pid;
int status;
pid=fork();
switch(pid){
case -1:
perror("fork is failed\n");
break;
case 0: //
execl("/bin/ls", "ls", "-al", NULL);
perror("execl is failed\n");
default: //
child_pid=wait(NULL); // SIGCHLD wait
printf("ls is complete\n");
printf("Parent PID (%d), Child PID(%d)\n", getpid(), child_pid);
exit(0);
}
}
쉘 프로그램은 fork(), exec() 계열, wait() 함수를 기반으로 작성 가능하다.
celina_shell.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#define MAXLINE 64
int main(int argc, char** argv){
char buf[MAXLINE];
// int proc_status;
pid_t pid;
printf("Celina_Shell ver 1.0\n");
while(1){
memset(buf, 0x00, MAXLINE);
//char* fgets(char* string, int n, FILE* stream)
fgets(buf, MAXLINE-1, stdin);
if(strncmp(buf, "exit\n", 5)==0){
break;
}
buf[strlen(buf)-1]=0x00; //enter NULL
pid=fork();
if(pid==0){
if(execlp(buf, buf, NULL)==-1){
printf("command execution is failed\n");
exit(0);
}
}
if(pid>0){
wait(NULL);
}
}
return 0;
}
pid_t wait(int *statloc), statloc
wait return value WIFEXITED
정상 종료 Child PID WIFEXITED(statloc)=true
하위 8비트 exit에 넘겨준 인자값 얻는다.(WIXITSTATUS(statloc)
비정상 종료 Child PID WIFEXITED(statloc)=true
비정상 종료 이유 WTERMSIG(statloc)
wait 함수 오류 -1 ECHILD:자식 프로세스가 없는 경우
EINTR: 시스템 콜이 인터럽트 되었을 때
void exit(int status)
pid_t wait(int* status) // PID
위와 같이 exit와 wait의 매개변수 자료형이 서로 다르다.(exit: int, wait: int*)
exit는 자식 프로세스에서 수행되는 경우 자식 프로세스가 직접 status에 상태를 넣어준다.

하지만 wait에서 status는 부모프로세스에서 수행되는 경우 부모 프로세스에서 직접 넣는 것이 아닌
운영체제로부터 SIGNAL에 대한 값을 전달 받는다. 따라서 포인터로 주소만 할당받고, 해당 주소에
운영체제가 자식 프로세스로부터의 SIGNAL에 대한 값을 기록한다.
status 정보를 통해 기본적인 자식 프로세스 관련 정보를 확인할 수 있다.
WIFEXITED
int WIFEXITED(status); // 0 .(true)
// WIFEXITED int !
wait_test.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
int main(void){
int pid;
int child_pid;
int status;
pid=fork();
switch(pid){
case -1:
perror("fork is failed\n");
break;
case 0:
execl("/bin/ls", "ls", "-al", NULL);
perror("execl is failed\n");
break;
default:
child_pid=wait(&status);
printf("Parent PID (%d), Child PID (%d)\n", getpid(), child_pid);
ret=WIFEXITED(status);
if(ret!=0){
printf("Child process is normally terminated\n");
}else{
printf("Child process is abnormally terminated\n");
}
exit(0)
}
}

celina@ubuntuserver:~/celina/test$ ./wait_test

total 280

drwx------ 3 celina celina  4096 Jul 28 03:38 .

drwx------ 3 celina celina  4096 Jul 23 02:34 ..

-rwxrwxr-x 1 celina celina 16704 Jul 27 03:48 argv_test

-rw-rw-r-- 1 celina celina   206 Jul 27 03:48 argv_test.c

-rwxrwxr-x 1 celina celina 16928 Jul 28 02:58 atexit_test

-rw-rw-r-- 1 celina celina   403 Jul 28 02:58 atexit_test.c

-rwxrwxr-x 1 celina celina 17144 Jul 27 05:18 celina_shell

-rw-rw-r-- 1 celina celina   726 Jul 27 05:17 celina_shell.c

-rwxrwxr-x 1 celina celina 17144 Jul 27 05:20 celina_shell2

-rw-rw-r-- 1 celina celina   727 Jul 27 05:19 celina_shell2.c

-rwx------ 1 celina celina   168 Jul 26 02:55 copy_test.txt

-rwxrwxr-x 1 celina celina 16832 Jul 27 03:54 execl_test

-rw-rw-r-- 1 celina celina   249 Jul 27 03:54 execl_test.c

-rwxrwxr-x 1 celina celina 16832 Jul 27 04:38 execve_test

-rw-rw-r-- 1 celina celina   249 Jul 27 04:38 execve_test.c

-rwxrwxr-x 1 celina celina 16784 Jul 27 03:19 fork_test

-rw-rw-r-- 1 celina celina   322 Jul 27 03:19 fork_test.c

-rwxrwxr-x 1 celina celina 16792 Jul 27 02:58 getpid

-rw-rw-r-- 1 celina celina   144 Jul 27 02:50 getpid.c

drwxrwxr-x 2 celina celina  4096 Jul 26 03:47 link_test

-rw-rw-r-- 1 celina celina     0 Jul 26 03:27 link_test.txt

-rwxrwxr-x 1 celina celina 16464 Jul 23 03:50 loop

-rw-rw-r-- 1 celina celina    74 Jul 23 03:50 loop.c

-rw-rw-r-- 1 celina celina   159 Jul 23 03:54 loop.cpp

-rwxrwxr-x 1 celina celina 17992 Jul 23 03:55 loop2

lrwxrwxrwx 1 celina celina    13 Jul 26 03:26 softlink_test.txt -> link_test.txt

-rwxrwxr-x 1 celina celina 17056 Jul 28 03:38 wait_test

-rw-rw-r-- 1 celina celina   728 Jul 28 03:38 wait_test.c

Parent PID (2670), Child PID (2671)

Child process is normally terminated

waitpid()

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int* statloc, int option);

wiatpid 함수의 첫 번째 인자

 의미

  pid가 -1 일 경우  (pid ==  -1)

   임의의 자식 프로세스를 기다림

  pid가 0 보다 클 경우 (pid > 0)

   프로세스 ID가 pid인 자식 프로세스를 기다림

  pid가 -1 보다 작을 경우 (pid < -1)

  프로세스 그룹 ID가 pid의 절댓값과 같은 자식 프로세스를 기다림

 pid가 0일 경우 (pid == 0)

 waitpid를 호출한 프로세스의 프로세스 그룹 PID와 같은 프로세스 그룹 ID를 가진 프로세스를 기다림 

세 번째 인자로 사용가능한 상수

 의미 

 WCONTINUED

 중단 되었다가 재개된 자식 프로세스의 상태를 받음

 WNOHANG 

 기다리는 PID가 종료되지 않아서 즉시 종료 상태를 회수 할 수 없는 상황에서 호출자는 차단되지 않고 반환값으로 0을 받음

 WUNTRACED 

 중단된 자식 프로세스의 상태를 받음